﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Offset Filter, i.e. use given coordinates to add an offset
    /// </summary>
    public class Offset : BaseImageFilter
    {
        protected Point[,] m_aOffset = null;

        /// <summary>
        /// Constructor
        /// </summary>
        public Offset()
        {
        }

        /// <summary>
        /// Constructor containing offset informations
        /// </summary>
        /// <param name="offset"></param>
        public Offset(Point[,] offset)
        {
            m_aOffset = offset;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_offset"></param>
        internal Offset(Offset _offset)
        {
            this.m_aOffset = _offset.m_aOffset;
        }
        
        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bSrc = (UnsafeBitmap)_bitmap.Clone();

            // GDI+ still lies to us - the return format is BGR, NOT RGB.
            BitmapData bmData = _bitmap.BitmapData; //b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmSrc = bSrc.BitmapData; //bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int scanline = bmData.Stride;

            IntPtr Scan0 = bmData.Scan0;
            IntPtr SrcScan0 = bmSrc.Scan0;

            int nBytesPerPixelCount = _bitmap.BytesPerPixelCount;
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                byte* pSrc = (byte*)(void*)SrcScan0;

                int nOffset = bmData.Stride - nBytesPerPixelCount * _bitmap.Width; //_bitmap.Width * 3; 
                int nWidth = _bitmap.Width;
                int nHeight = _bitmap.Height;

                int xOffset, yOffset;

                int y = 0;
                int x = 0;

                for (y = 0; y < nHeight; ++y)
                {
                    for (x = 0; x < nWidth; ++x)
                    {
                        xOffset = m_aOffset[x, y].X;
                        yOffset = m_aOffset[x, y].Y;

                        p[0] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3)];
                        p[1] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 1];
                        p[2] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 2];

                        p += nBytesPerPixelCount; //p += 3;
                    }
                    p += nOffset;
                }
            }

            bSrc.Dispose(); //bSrc.UnlockBits(bmSrc);
            return _bitmap;
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Offset(this);
        }
    }
}
